// ==UserSubscribe==
// @name         小红书主页数据采集工具（精准XPath提取）
// @namespace    http://tampermonkey.net/
// @version      2.0.4
// @description  仅使用网页元素采集，使用XPath精准提取数据
// @author       子风
// @match        https://www.xiaohongshu.com/user*
// @match        https://www.xiaohongshu.com/search_result*
// @grant        GM_download
// @grant        GM_xmlhttpRequest
// @require      https://cdn.jsdelivr.net/npm/xlsx/dist/xlsx.full.min.js
// @scriptUrl    https://gitee.com/chuzifeng/script-cat-script/raw/master/%E5%B0%8F%E7%BA%A2%E4%B9%A6%E4%B8%BB%E9%A1%B5%E9%87%87%E9%9B%86.js
// ==/UserSubscribe==

(function () {
    'use strict';

    // 脚本版本号
    const SCRIPT_VERSION = '2.0.4';

    let dataList = [];  // 存储采集到的数据
    let collectedIndexes = new Set(); // 记录已采集的 data-index，避免重复
    let autoMode = false;  // 自动滚动模式
    let manualMode = false;  // 手动采集模式
    let countDisplay;
    let observer = null;  // MutationObserver 实例

    // **获取元素文本工具函数**
    function getElementText(xpath, context = document) {
        let element = document.evaluate(xpath, context, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        return element ? element.textContent.trim() : "";
    }

    // **获取属性值工具函数**
    function getElementAttribute(xpath, attribute, context = document) {
        let element = document.evaluate(xpath, context, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
        return element ? element.getAttribute(attribute) : "";
    }

    // **添加本地存储相关函数**
    function saveToLocalStorage() {
        try {
            localStorage.setItem('xhs_collector_data', JSON.stringify(dataList));
            localStorage.setItem('xhs_collector_indexes', JSON.stringify(Array.from(collectedIndexes)));
            console.log('数据已保存到本地存储');
        } catch (error) {
            console.error('保存到本地存储失败:', error);
        }
    }

    function loadFromLocalStorage() {
        try {
            const savedData = localStorage.getItem('xhs_collector_data');
            const savedIndexes = localStorage.getItem('xhs_collector_indexes');
            
            if (savedData) {
                dataList = JSON.parse(savedData);
                console.log(`从本地存储恢复了 ${dataList.length} 条数据`);
            }
            
            if (savedIndexes) {
                collectedIndexes = new Set(JSON.parse(savedIndexes));
                console.log(`从本地存储恢复了 ${collectedIndexes.size} 个索引`);
            }

            updateDataCount();
        } catch (error) {
            console.error('从本地存储加载失败:', error);
        }
    }

    function clearLocalStorage() {
        localStorage.removeItem('xhs_collector_data');
        localStorage.removeItem('xhs_collector_indexes');
        console.log('已清除本地存储的数据');
    }

    // **采集当前页面的所有 note-item 数据**
    function extractPageData() {
        let items = document.querySelectorAll('.note-item');
        let newDataAdded = false;
        
        items.forEach(item => {
            let dataIndex = item.getAttribute('data-index');
            if (!dataIndex || collectedIndexes.has(dataIndex)) return;

            try {
                let username = getElementText(".//*[@class='author-wrapper']/a/span", item);
                let title = getElementText(".//*[@class='title']/span", item);
                let link = getElementAttribute(".//*[@class='cover mask ld']", "href", item);
                let noteId = getElementAttribute(".//*[starts-with(@href, '/explore')]", "href", item);

                // 处理链接格式
                if (link) {
                    // 统一转换 /search_result/ 为 /explore/
                    link = link.replace('/search_result/', '/explore/');

                    // 确保完整的URL
                    if (!link.startsWith('http')) {
                        link = 'https://www.xiaohongshu.com' + link;
                    }
                }


                if (noteId) {
                    noteId = noteId.split('/')[2];
                }

                if (username && title && link && noteId) {
                    dataList.push({ 序号: dataIndex, 用户名: username, 标题: title, 链接ID: noteId, 链接: link });
                    collectedIndexes.add(dataIndex);
                    console.log(`采集到新数据: ${title}`);
                    newDataAdded = true;
                    updateDataCount();
                }
            } catch (error) {
                console.log(`数据提取出错: ${error.message}`);
            }
        });

        // 如果有新数据添加，就保存到本地存储
        if (newDataAdded) {
            saveToLocalStorage();
        }
    }

    // **滚动加载数据**
    async function autoScroll() {
        while (autoMode) {
            // 检查是否存在"THE END"元素
            const endElement = document.evaluate("//*[contains(text(), 'THE END')]", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
            if (endElement) {
                console.log("检测到页面底部'THE END'标记，停止自动采集");
                
                // 关闭自动模式
                autoMode = false;
                
                // 更新自动采集按钮状态
                const autoButton = document.querySelector("button:nth-child(4)");
                if (autoButton) {
                    autoButton.innerText = "自动采集：关闭";
                    autoButton.style.background = "#2196F3";
                }
                
                // 关闭手动模式
                manualMode = false;
                
                // 更新手动采集按钮状态
                const manualButton = document.querySelector("button:nth-child(3)");
                if (manualButton) {
                    manualButton.innerText = "手动采集：关闭";
                    manualButton.style.background = "#4CAF50";
                }
                
                // 清理定时器
                if (observer) {
                    clearInterval(observer);
                    observer = null;
                }
                
                // 最后再采集一次数据
                extractPageData();
                break;
            }

            // 执行滚动
            const viewportHeight = window.innerHeight;
            const currentScroll = window.pageYOffset;
            const scrollStep = viewportHeight * 0.5; // 每次滚动50%视窗高度

            window.scrollTo({
                top: currentScroll + scrollStep,
                behavior: 'smooth'
            });

            // 采集数据
            extractPageData();

            // 等待滚动和加载完成
            await new Promise(resolve => setTimeout(resolve, 800));
        }
    }

    // **更新 UI 采集数量**
    function updateDataCount() {
        countDisplay.innerText = `采集数量：${dataList.length} 条`;
    }

    // **下载 Excel 文件**
    function downloadExcel() {
        let wb = XLSX.utils.book_new();
        let ws = XLSX.utils.json_to_sheet(dataList);
        XLSX.utils.book_append_sheet(wb, ws, "数据");
        let blob = new Blob([XLSX.write(wb, { bookType: "xlsx", type: "array" })], { type: "application/octet-stream" });
        let a = document.createElement("a");
        a.href = URL.createObjectURL(blob);
        a.download = "小红书数据.xlsx";
        a.click();
    }

    // **创建监测新数据的 Observer**
    function createDataObserver() {
        if (observer) {
            clearInterval(observer);
        }

        let lastFirstIndex = null;
        let lastLastIndex = null;
        
        observer = setInterval(() => {
            if (manualMode) {
                // 获取当前可见的所有数据项
                let items = document.querySelectorAll('.note-item');
                if (!items.length) return;

                // 获取第一条和最后一条数据的索引
                let firstItem = items[0];
                let lastItem = items[items.length - 1];
                let firstIndex = firstItem.getAttribute('data-index');
                let lastIndex = lastItem.getAttribute('data-index');

                // 检查数据变化
                if (firstIndex !== lastFirstIndex || lastIndex !== lastLastIndex) {
                    console.log('检测到数据更新，开始采集新数据...');
                    
                    // 遍历所有项进行采集
                    items.forEach(item => {
                        let dataIndex = item.getAttribute('data-index');
                        if (!dataIndex || collectedIndexes.has(dataIndex)) return;

                        try {
                            let username = getElementText(".//*[@class='author-wrapper']/a/span", item);
                            let title = getElementText(".//*[@class='title']/span", item);
                            let link = getElementAttribute(".//*[@class='cover mask ld']", "href", item);
                            let noteId = getElementAttribute(".//*[starts-with(@href, '/explore')]", "href", item);

                        // 处理链接格式
                        if (link) {
                            // 统一转换 /search_result/ 为 /explore/
                            link = link.replace('/search_result/', '/explore/');

                            // 确保完整的URL
                            if (!link.startsWith('http')) {
                                link = 'https://www.xiaohongshu.com' + link;
                            }
                        }

                            if (noteId) {
                                noteId = noteId.split('/')[2];
                            }

                            if (username && title && link && noteId) {
                                dataList.push({ 序号: dataIndex, 用户名: username, 标题: title, 链接ID: noteId, 链接: link });
                                collectedIndexes.add(dataIndex);
                                console.log(`采集到新数据: ${title}`);
                                updateDataCount();
                            }
                        } catch (error) {
                            console.log(`数据提取出错: ${error.message}`);
                        }
                    });
                }

                // 更新首尾索引记录
                lastFirstIndex = firstIndex;
                lastLastIndex = lastIndex;

            } else {
                clearInterval(observer);
                observer = null;
            }
        }, 1000); // 改为每秒检测一次
    }

    // **添加验证码检测**
    function setupCaptchaDetection() {
        // 监视 URL 变化
        let lastUrl = window.location.href;
        setInterval(() => {
            if (window.location.href !== lastUrl) {
                // 如果 URL 包含验证码相关字符串，说明跳转到了验证码页面
                if (window.location.href.includes('captcha') || window.location.href.includes('verify')) {
                    console.log('检测到验证码页面，已自动保存数据');
                    saveToLocalStorage();
                    
                    // 关闭自动采集模式
                    autoMode = false;
                    const autoButton = document.querySelector("button:nth-child(4)");
                    if (autoButton) {
                        autoButton.innerText = "自动采集：关闭";
                        autoButton.style.background = "#2196F3";
                    }
                }
                lastUrl = window.location.href;
            }
        }, 1000);
    }

    // **修改 UI 控件创建函数**
    function createUIPanel() {
        // 创建主面板
        let panel = document.createElement("div");
        panel.style.position = "fixed";
        panel.style.top = "10px";
        panel.style.right = "10px";
        panel.style.zIndex = "9999";
        panel.style.background = "rgba(0, 0, 0, 0.8)";
        panel.style.color = "white";
        panel.style.padding = "20px";
        panel.style.borderRadius = "10px";
        panel.style.width = "300px";
        panel.style.display = "flex";
        panel.style.flexDirection = "column";
        panel.style.gap = "10px";
        panel.style.transition = "transform 0.3s ease-in-out";
        panel.id = "collectorPanel";

        // 修改开关按钮样式和位置
        let toggleButton = document.createElement("div");
        toggleButton.innerHTML = "展开/收起 ▼";
        toggleButton.style.position = "fixed";
        toggleButton.style.top = "10px";
        toggleButton.style.right = "320px";
        toggleButton.style.zIndex = "10000";
        toggleButton.style.background = "rgba(33, 150, 243, 0.9)"; // 更改为蓝色背景
        toggleButton.style.color = "white";
        toggleButton.style.padding = "8px 20px"; // 增加内边距
        toggleButton.style.cursor = "pointer";
        toggleButton.style.borderRadius = "4px 0 0 4px"; // 添加圆角
        toggleButton.style.boxShadow = "0 2px 5px rgba(0,0,0,0.2)"; // 添加阴影
        toggleButton.style.userSelect = "none"; // 防止文字被选中
        toggleButton.style.display = "block"; // 确保显示
        toggleButton.style.width = "auto"; // 自适应宽度

        // 增强悬停效果
        toggleButton.onmouseover = () => {
            toggleButton.style.background = "rgba(33, 150, 243, 1)";
        };
        toggleButton.onmouseout = () => {
            toggleButton.style.background = "rgba(33, 150, 243, 0.9)";
        };

        // 面板展开/收起逻辑优化
        let isPanelVisible = true;
        toggleButton.onclick = () => {
            isPanelVisible = !isPanelVisible;
            panel.style.transform = isPanelVisible ? "translateX(0)" : "translateX(320px)";
            toggleButton.innerHTML = isPanelVisible ? "展开/收起 ▼" : "展开/收起 ▲";
            toggleButton.style.right = isPanelVisible ? "320px" : "10px";
            // 添加过渡效果
            toggleButton.style.transition = "right 0.3s ease-in-out";
        };

        let title = document.createElement("h2");
        title.innerText = "小红书采集器";
        title.style.textAlign = "center";
        title.style.fontSize = "18px";
        panel.appendChild(title);

        countDisplay = document.createElement("div");
        countDisplay.innerText = "采集数量：0 条";
        countDisplay.style.textAlign = "center";
        panel.appendChild(countDisplay);

        let manualButton = document.createElement("button");
        manualButton.innerText = "手动采集：关闭";
        manualButton.style.padding = "10px";
        manualButton.style.border = "none";
        manualButton.style.background = "#4CAF50";
        manualButton.style.color = "white";
        manualButton.style.cursor = "pointer";
        manualButton.onclick = function() {
            manualMode = !manualMode;
            manualButton.innerText = `手动采集：${manualMode ? "开启" : "关闭"}`;
            manualButton.style.background = manualMode ? "#f44336" : "#4CAF50";
            
            if (manualMode) {
                // 开启手动模式时，确保自动模式关闭
                autoMode = false;
                const autoButton = document.querySelector("button:nth-child(4)");
                if (autoButton) {
                    autoButton.innerText = "自动采集：关闭";
                    autoButton.style.background = "#2196F3";
                }
                
                // 立即执行一次采集
                extractPageData();
                // 创建定时检测
                createDataObserver();
            } else {
                // 关闭手动模式时，清除定时器
                if (observer) {
                    clearInterval(observer);
                    observer = null;
                }
            }
        };
        panel.appendChild(manualButton);

        let autoButton = document.createElement("button");
        autoButton.innerText = "自动采集：关闭";
        autoButton.style.padding = "10px";
        autoButton.style.border = "none";
        autoButton.style.background = "#2196F3";
        autoButton.style.color = "white";
        autoButton.style.cursor = "pointer";
        autoButton.onclick = async function () {
            autoMode = !autoMode;
            autoButton.innerText = `自动采集：${autoMode ? "开启" : "关闭"}`;
            autoButton.style.background = autoMode ? "#f44336" : "#2196F3";
            if (autoMode) await autoScroll();
        };
        panel.appendChild(autoButton);

        let clearButton = document.createElement("button");
        clearButton.innerText = "清空数据";
        clearButton.style.padding = "10px";
        clearButton.style.border = "none";
        clearButton.style.background = "#FF5722";
        clearButton.style.color = "white";
        clearButton.style.cursor = "pointer";
        clearButton.onclick = function () {
            dataList = [];
            collectedIndexes.clear();
            updateDataCount();
        };
        panel.appendChild(clearButton);

        let downloadButton = document.createElement("button");
        downloadButton.innerText = "下载Excel";
        downloadButton.style.padding = "10px";
        downloadButton.style.border = "none";
        downloadButton.style.background = "#FF9800";
        downloadButton.style.color = "white";
        downloadButton.style.cursor = "pointer";
        downloadButton.onclick = downloadExcel;
        panel.appendChild(downloadButton);

        // 添加恢复数据按钮
        let restoreButton = document.createElement("button");
        restoreButton.innerText = "恢复数据";
        restoreButton.style.padding = "10px";
        restoreButton.style.border = "none";
        restoreButton.style.background = "#9C27B0";
        restoreButton.style.color = "white";
        restoreButton.style.cursor = "pointer";
        restoreButton.onclick = loadFromLocalStorage;
        panel.appendChild(restoreButton);

        // 确保按钮在面板之前添加到文档中
        document.body.appendChild(toggleButton);
        document.body.appendChild(panel);
    }

    // **修改窗口卸载时的清理**
    window.addEventListener('unload', () => {
        if (observer) {
            clearInterval(observer);
            observer = null;
        }
    });

    // **添加版本检查函数**
    async function checkUpdate() {
        const currentVersion = SCRIPT_VERSION;
        const updateUrl = 'https://gitee.com/chuzifeng/script-cat-script/raw/master/小红书主页采集.js';
        
        try {
            const response = await new Promise((resolve, reject) => {
                GM_xmlhttpRequest({
                    method: 'GET',
                    url: updateUrl,
                    onload: (response) => resolve(response),
                    onerror: (error) => reject(error)
                });
            });

            const scriptContent = response.responseText;
            const versionMatch = scriptContent.match(/@version\s+(\d+\.\d+\.\d+)/);
            
            if (versionMatch && versionMatch[1]) {
                const latestVersion = versionMatch[1];
                
                if (latestVersion !== currentVersion) {
                    const shouldUpdate = confirm(`发现新版本 ${latestVersion}，当前版本 ${currentVersion}。\n是否更新？`);
                    if (shouldUpdate) {
                        window.location.href = updateUrl;
                        return false; // 停止脚本执行
                    }
                }
            }
            return true; // 继续执行脚本
        } catch (error) {
            console.warn('版本检查失败:', error);
            return true; // 出错时继续执行脚本
        }
    }

    // **修改启动函数**
    window.onload = async function () {
        const shouldContinue = await checkUpdate();
        if (!shouldContinue) return;

        createUIPanel();
        setupCaptchaDetection();
        
        // 检查是否有本地存储的数据
        loadFromLocalStorage();
        
        setTimeout(() => {
            console.log('开始首次数据检测...');
            extractPageData();
        }, 2000);
    };
})();
